import {Button, DatePicker, Input, InputRef, Space, TableColumnProps, TablePaginationConfig, theme} from "antd";
import React, {Key, useRef, useState} from "react";
import {CalendarOutlined, SearchOutlined} from "@ant-design/icons";
import Highlighter from "react-highlight-words"
import {ColumnFilterItem, FilterValue, SorterResult} from "antd/es/table/interface";
import {createUseStyles} from "react-jss";
import {GlobalToken} from "antd/es/theme/interface";
import {Order, Pageable, Sort} from "../utils/Page";
import dayjs, {Dayjs} from "dayjs";
import Exceljs from "exceljs";
import {LocalDate, LocalDateTime, OffsetDateTime} from "@js-joda/core";
import {Worksheet} from "exceljs";
import {DATE_WITH_TIME_FORMATER, YEAR_MONTH_DAY_FORMATER} from "./Date";
import ArrayUtils from "@zxy-cn/array-utils";
import {SubGoodsResponse} from "../apis/GoodsApi";
import {DisableCondition} from "../components/dashboardPages/GoodsPage";
import {ColumnType} from "antd/lib/table";

export interface BuildTableSearchTextColumnOptions<DT> {
    placeholder?: string
    handleSearch?: (text: string|undefined, dataIndex: keyof DT) => Promise<void> | void
}

export type DateRange = [Dayjs, Dayjs]

export interface BuildTableSearchDateRangeColumnOptions<DT> {
    placeholder?: string
    handleSearch: (value: DateRange | undefined, dataIndex: keyof DT) => Promise<void> | void
}


const useStyles = createUseStyles({
    searchIconContainer: (props: GlobalToken & {
        hasValue?: boolean
    }) => ({
        borderColor: props.hasValue ? props.colorPrimaryBorder : undefined,
        borderRadius: "4px",
        borderWidth: "1px",
        borderStyle: props.hasValue ? "solid" : undefined,
        padding: "0 8px"
    })
})

export function useTableFilterDateRangeColumnProps<DT, K extends keyof DT = keyof DT>(dataIndex: K, options: BuildTableSearchDateRangeColumnOptions<DT>): TableColumnProps<DT> {
    const ref = useRef<any>(null);
    const [value, setValue] = useState<[Dayjs, Dayjs]>()
    return {
        filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters, close}) => {

            async function handleSearch(value: DateRange | undefined, dataIndex: K) {
                setValue(value)
                confirm()

                if (!options.handleSearch) {
                    return Promise.resolve()
                } else {
                    let handleSearchResult = options.handleSearch(value, dataIndex);
                    if (handleSearchResult instanceof Promise) {
                        await handleSearchResult
                    } else {
                        return Promise.resolve()
                    }
                }


            }

            function selectedKeysToValue(selectedKeys: Key[]) {
                return selectedKeys.length == 2 ? [dayjs(selectedKeys[0] as string), dayjs(selectedKeys[1] as string)] as [Dayjs, Dayjs] : undefined
            }

            return (
                <div style={{padding: 8}} onKeyDown={(e) => e.stopPropagation()}>
                    <DatePicker.RangePicker
                        allowClear={false}
                        size={"small"}
                        ref={ref}
                        value={selectedKeysToValue(selectedKeys)}
                        onChange={(dates) => {
                            let mySelectedKeys = dates?.map(it => it?.format()).filter(it => !!it).map(it => it!!) ?? [];
                            setSelectedKeys(mySelectedKeys)
                        }}
                        style={{marginBottom: 8, display: 'block'}}
                    />
                    <Space>
                        <Button
                            type="primary"
                            onClick={() => {
                                // setSelectedKeys
                                let selectedKeysToValue1 = selectedKeysToValue(selectedKeys);
                                handleSearch(selectedKeysToValue1, dataIndex)
                            }}
                            icon={<SearchOutlined/>}
                            size="small"
                            style={{width: 90}}
                        >
                            查询
                        </Button>
                        <Button
                            onClick={() => clearFilters && (() => {
                                clearFilters()
                                setValue(undefined)
                            })()}
                            size="small"
                            style={{width: 90}}
                        >
                            重置
                        </Button>
                        <Button
                            type="link"
                            size="small"
                            onClick={() => {
                                close();
                            }}
                        >
                            关闭
                        </Button>
                    </Space>
                </div>
            );
        },
        filterIcon: (filtered: boolean) => (
            <CalendarOutlined style={{color: value ? '#1677ff' : undefined}}/>
        ),
        onFilterDropdownOpenChange: (visible) => {
            if (visible) {
                setTimeout(() => ref.current?.focus(), 100);
            }
        },
    };
}

export function useTableSearchTextColumnProps<DT, K extends keyof DT = keyof DT>(dataIndex: K, options: BuildTableSearchTextColumnOptions<DT>): TableColumnProps<DT> {
    const searchInput = useRef<InputRef>(null);
    const [searchText, setSearchText] = useState<string>("")
    const themeData = theme.useToken();
    let styles = useStyles({...themeData.token, hasValue: !!searchText});

    // @ts-ignore
    return {
        filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters, close}) => {
            async function handleSearch(text: string, dataIndex: K) {
                setSearchText(text)
                confirm()
                if (!options.handleSearch) return Promise.resolve()
                let handleSearchResult = options.handleSearch(text, dataIndex);
                if (handleSearchResult instanceof Promise) {
                    await handleSearchResult
                } else {
                    return Promise.resolve()
                }
            }

            return (
                <div style={{padding: 8}} onKeyDown={(e) => e.stopPropagation()}>
                    <Input
                        ref={searchInput}
                        placeholder={options.placeholder}
                        value={selectedKeys[0]}
                        onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
                        onPressEnter={() => handleSearch((selectedKeys as string[])[0], dataIndex)}
                        style={{marginBottom: 8, display: 'block'}}
                    />
                    <Space>
                        <Button
                            type="primary"
                            onClick={() => {
                                return handleSearch((selectedKeys as string[])[0], dataIndex);
                            }}
                            icon={<SearchOutlined/>}
                            size="small"
                            style={{width: 90}}
                        >
                            查询
                        </Button>
                        <Button
                            onClick={() =>{
                                clearFilters?.()
                                setSearchText("")
                                setSelectedKeys([""])
                            }}
                            size="small"
                            style={{width: 90}}
                        >
                            重置
                        </Button>
                        <Button
                            type="link"
                            size="small"
                            onClick={() => {
                                close();
                            }}
                        >
                            关闭
                        </Button>
                    </Space>
                </div>
            );
        },
        filterIcon: (filtered: boolean) => (
            <Space className={styles.searchIconContainer}>
                <SearchOutlined style={{color: filtered ? '#1677ff' : undefined}}/>
                {searchText ? <span>{searchText}</span> : undefined}
            </Space>
        ),
        onFilterDropdownOpenChange: (visible) => {
            if (visible) {
                setTimeout(() => searchInput.current?.focus(), 100);
            }
        },
        render: (text) =>
            searchText ? (
                // @ts-ignore
                <Highlighter
                    highlightStyle={{backgroundColor: '#ffc069', padding: 0}}
                    searchWords={[searchText]}
                    autoEscape
                    textToHighlight={text ? text.toString() : ''}
                />
            ) : (
                text
            ),
    };
}

export function castFilterValue(value: FilterValue | null): string | undefined {
    return value ? ((value as string[])[0] || undefined ): undefined
}

export function sorterToSort(sorter: SorterResult<any> | SorterResult<any>[] | undefined) {
    if (!sorter) {
        return Sort.empty
    } else {
        let orders;
        if (sorter instanceof Array) {
            orders = sorter.map(sortResultToOrder)
        } else {
            orders = [sortResultToOrder(sorter)]
        }
        return new Sort(orders.filter(it => !!it).map(it => it!!))
    }
}

function sortResultToOrder(sorterResult: SorterResult<any>) {
    if (sorterResult.field) {
        return new Order(sorterResult.field as string, sorterResult.order ? (sorterResult.order === "ascend") : undefined)
    } else {
        return null
    }
}

export function mergePaginationAndPageable(tablePagination: TablePaginationConfig, pageable: Pageable, sorter?: SorterResult<any> | SorterResult<any>[]) {
    return Pageable.from(
        pageable,
        {
            page: tablePagination.current,
            size: tablePagination.pageSize,
            sort: sorterToSort(sorter)
        }
    )
}



export async function convertAntdTableToExcelWithOptions<T>(columns: ExportedColumnProps<T>[], items: T[], options:{selectedColumns?: string[],postHandler?:(worksheet: Exceljs.Worksheet, data:T[])=>void } = {},){
    const selectedColumns = options.selectedColumns
    let workbook = new Exceljs.Workbook();
    let worksheet = workbook.addWorksheet("Sheet 1");
    columns = columns.filter(it => !it.ignoreInExcel)
    let renderColumns = columns.filter(value => selectedColumns?.includes(value.title as string)
        || selectedColumns?.includes(value.dataIndex as string) || !selectedColumns);
    worksheet.columns = renderColumns.map(column => {
        return {
            ...column,
            width: column.excelWidth,
            header: column.title as string,
            key: (column.key as string) ?? column.dataIndex as string,
        }
    })

    worksheet.addRows(items.map(item => {
        const object = {};

        function isValidValue(value: any) {
            return typeof value == "string" || typeof value == "number" || typeof value == "undefined";
        }

        for (let i = 0; i < renderColumns.length; i++) {
            let column = renderColumns[i];
            let key = (column.key as string) ?? column.dataIndex as string;
            let value = item[key];

            if (column.generateExcelValue) {
                object[key] = column.generateExcelValue(value, item)
            } else {

                if (!!column.render) {
                    let renderValue = column.render!(value, item, i);

                    if (isValidValue(renderValue)) {
                        object[key] = renderValue;
                        continue
                    }
                }
                if (isValidValue(value)) {
                    object[key] = value;
                } else {
                    throw new Error(`unknown value:${value} key:${key}`);
                }
            }
        }
        return object
    }))

    options.postHandler?.(worksheet,items)

    for (let i = 1; i <= (worksheet.lastRow?.number??0); i++) {
        for (let j = 1; j <= (worksheet.lastColumn?.number??0); j++) {
            let cell = worksheet.getRow(i).getCell(j);
            cell.border = {
                top: {style: 'thin'},
                left: {style: 'thin'},
                bottom: {style: 'thin'},
                right: {style: 'thin'},
            }
        }
    }

    function adjustColumnWidth(worksheet: Worksheet) {
        worksheet.columns.forEach(column => {
            let lengthArray: number[] = [];
            column.eachCell && column.eachCell((cell, _) => {
                let string = cell.value?.toString();
                if (string) {
                    let match = string.match(/[\u4e00-\u9fa5]/);
                    lengthArray.push(string.length + (match?.join("")?.length ?? 0) + 2)
                }
            })
            const maxLength = Math.max(...lengthArray);
            if (maxLength) {
                column.width = maxLength;
            }
        });
    }

    adjustColumnWidth(worksheet)

    return workbook;
}

export async function convertAntdTableToExcel<T>(columns: ExportedColumnProps<T>[], items: T[], selectedColumns?: string[]) {
    return convertAntdTableToExcelWithOptions(columns, items, {selectedColumns})
}

export interface ExportedColumnProps<T> extends TableColumnProps<T> {
    generateExcelValue?: (value: any|undefined, record: T) => string|number|undefined
    excelWidth?: Exceljs.Column["width"]
    ignoreInExcel?: boolean
}

export const AntdTableColumnFilterSearches = {
    columnFilterByOptionText: (input: string, record: ColumnFilterItem) => {
        return (record.text as string).includes(input)
    },
}

export const AntdTableColumnRenders = {
    dateTimeStringColumnRender: (value?: string) => {
        if (value) {
            return OffsetDateTime.parse(value).format(DATE_WITH_TIME_FORMATER);
        } else {
            return null
        }
    },
    dateTimeColumnRender(value?:OffsetDateTime){
        return value?value.format(DATE_WITH_TIME_FORMATER):null
    },
    dateColumnRender: (value?: string) => {
        if (value) {
            return OffsetDateTime.parse(value).format(YEAR_MONTH_DAY_FORMATER);
        } else {
            return null
        }
    },
}

export function buildSortCompareFunction<T>(fieldName:keyof T){
    return (a: T, b: T,) => {
        let valueA = a[fieldName];
        let valueB = b[fieldName];
        if (typeof valueA === "string" && typeof valueB === "string") {
            return valueA.localeCompare(valueB);
        } else if (typeof valueA === "number" && typeof valueB === "number") {
            return valueA - valueB
        } else {
            return JSON.stringify(valueA).localeCompare(JSON.stringify(valueB))
        }
    }
}


export function buildDisableOrEnableColumnProps<T  extends {disable:boolean}>(value?:DisableCondition) :ColumnType<T>{
    return {
        key: "disable",
        title: "状态",
        dataIndex: "disable",
        render: (value) => {
            if (value) {
                return "禁用"
            } else {
                return "启用"
            }
        },
        filterMultiple: false,
        defaultFilteredValue:["0"],
        filteredValue:value,
        filterResetToDefaultFilteredValue:true,
        filters: [{
            value: "-1",
            text: "全部",
        }, {
            value: "1",
            text: "禁用",
        }, {
            value: "0",
            text: "启用"
        }],
    }
}
